﻿using SharpCompress.Archives;
using SharpCompress.Common;
using SQLite;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Linq;
using Teriri.File.Server.Entity;
using Teriri.File.Server.Filter;
using TouchSocket.Dmtp.Rpc;
using TouchSocket.Rpc;

namespace Teriri.File.Server.Service
{
    [ServiceFilterAttribute]
    public class FileManageService : RpcServer
    {
        [Description("获取指定目录文件")]
        [DmtpRpc("FileManage_GetFilesAndFolders")]
        public List<FileInfoDto> GetFilesAndFolders(string path)
        {
            path = PathFormart(path);

            if (path == Path.DirectorySeparatorChar.ToString())
                path = GetRootPath();
            else
                path = Path.Combine(GetRootPath(), path);

            var fileInfoList = new List<FileInfoDto>();

            var entries = Directory.GetFileSystemEntries(path);

            var dirList = new List<FileInfoDto>();
            var fileList = new List<FileInfoDto>();

            foreach (var entry in entries)
            {
                var fileInfo = new FileInfo(entry);

                // 判断是文件还是文件夹
                if (System.IO.File.GetAttributes(entry).HasFlag(FileAttributes.Directory))
                {
                    dirList.Add(new FileInfoDto
                    {
                        Name = Path.GetFileName(entry),
                        Type = "dir",
                        IsDir = true,
                        CreateTime = fileInfo.CreationTime,
                        UpdateTime = fileInfo.LastWriteTime
                    });
                }
                else
                {
                    fileList.Add(new FileInfoDto
                    {
                        Name = Path.GetFileName(entry),
                        Type = fileInfo.Extension.ToLower().Replace(".", ""),
                        Url = entry,
                        UpdateTime = fileInfo.LastWriteTime,
                        CreateTime = fileInfo.CreationTime,
                        Size = this.GetFileSizeStr(fileInfo.Length),
                    });
                }
            }

            fileInfoList.AddRange(fileList.OrderByDescending(x => x.UpdateTime).ToList());
            fileInfoList.AddRange(dirList.OrderByDescending(x => x.UpdateTime).ToList());

            return fileInfoList;
        }

        [Description("创建文件夹")]
        [DmtpRpc("FileManage_CreateFolder")]
        public string CreateFolder(string path, string folderName)
        {
            path = PathFormart(path);
            var fullPath = Path.Combine(GetRootPath(), path, folderName);
            Directory.CreateDirectory(fullPath);
            return null;
        }

        [Description("解压指定文件")]
        [DmtpRpc("FileManage_DecompressingFile")]
        public string DecompressingFile(string path)
        {
            path = PathFormart(path);

            if (System.IO.File.Exists(path) == false)
                throw new Exception("文件不存在，请检查！");

            path = Path.Combine(GetRootPath(), path);

            var fileInfo = new FileInfo(path);
            var extractPath = Path.Combine(fileInfo.Directory.FullName, Path.GetFileNameWithoutExtension(path));

            if (!Directory.Exists(extractPath))
                Directory.CreateDirectory(extractPath);

            var log = new DecompressingFileLogEntity()
            {
                Id = Guid.NewGuid().ToString(),
                DecompressingPath = extractPath,
                FileName = Path.GetFileName(path),
                FilePath = path,
                SourceSize = Math.Round((double)fileInfo.Length / (1024 * 1024), 2),
                StartTime = DateTime.Now,
                StartTimeStr = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"),
            };

            using (var connection = new SQLiteConnection("log.db"))
                connection.Insert(log);

            Task.Run(() =>
            {
                var sourcePath = log.FilePath;
                var extractPath = log.DecompressingPath;

                try
                {
                    this.DecompressingFile(sourcePath, extractPath, fileInfo.Extension.ToLower());

                    log.Successed = true;
                    log.DecompressingSize = Math.Round(CalculateFolderSize(extractPath) / (1024 * 1024), 2);

                }
                catch (Exception ex)
                {
                    log.Successed = false;
                    log.ExceptionMessage = ex.Message;
                    log.ExceptionStackTrace = ex.StackTrace;
                }
                finally
                {
                    log.EndTime = DateTime.Now;
                    log.EndTimeStr = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
                    log.UsedSeconds = (log.EndTime.Value - log.StartTime.Value).TotalSeconds.ToString("0.00");
                    log.DecompressingRatio = log?.SourceSize / log?.DecompressingSize;

                    using (var connection = new SQLiteConnection("log.db"))
                        connection.Update(log);
                }
            });

            return null;
        }

        /// <summary>
        /// 获取基础目录
        /// </summary>
        [Description("获取基础目录")]
        [DmtpRpc("FileManage_GetRootPath")]
        public string GetRootPath()
        {
            var rootPath = ConfigHelper.GetConfig<ServerConfig>().FileRootPath;
            if (!rootPath.EndsWith(Path.DirectorySeparatorChar))
                rootPath += Path.DirectorySeparatorChar;

            return rootPath;
        }

        /// <summary>
        /// 解压指定目录下所有压缩包文件
        /// </summary>
        [Description("解压指定目录下所有压缩包文件")]
        [DmtpRpc("FileManage_DecompressingDirFiles")]
        public string DecompressingDirFiles(string path)
        {
            path = PathFormart(path);

            if (System.IO.Directory.Exists(path) == false)
                throw new Exception("指定目录不存在，请检查！");

            // 递归文件夹，得到所有疑似压缩包的文件
            var files = new List<System.IO.FileInfo>();
            this.GetDecompressFilesByDir(path, files);

            foreach (var fileInfo in files)
            {
                var extractPath = Path.Combine(fileInfo.Directory.FullName, Path.GetFileNameWithoutExtension(path));

                try
                {
                    this.DecompressingFile(fileInfo.FullName, extractPath, fileInfo.Extension);
                }
                catch (Exception ex)
                {

                }
            }

            return null;
        }

        #region 工具方法

        /// <summary>
        /// 解压文件
        /// </summary>
        private void DecompressingFile(string sourcePath, string extractPath, string extension)
        {
            if (extension.EndsWith("zip"))
            {
                ZipFile.ExtractToDirectory(sourcePath, extractPath);
            }
            else if (extension.EndsWith("7z") || extension.EndsWith("rar"))
            {
                using var archive = ArchiveFactory.Open(sourcePath);
                foreach (var entry in archive.Entries)
                {
                    if (entry.IsDirectory)
                        continue;

                    entry.WriteToDirectory(extractPath, new ExtractionOptions()
                    {
                        ExtractFullPath = true,
                        Overwrite = true,
                    });
                }
            }
            else
            {
                throw new Exception("暂不支持此压缩格式：" + extension);
            }
        }

        /// <summary>
        /// 获取指定目录所有文件的大小
        /// </summary>
        private double CalculateFolderSize(string folderPath, double totalSize = 0)
        {
            DirectoryInfo directoryInfo = new DirectoryInfo(folderPath);
            FileInfo[] fileInfos = directoryInfo.GetFiles();

            foreach (FileInfo fileInfo in fileInfos)
                totalSize += fileInfo.Length;

            DirectoryInfo[] subDirectories = directoryInfo.GetDirectories();

            foreach (DirectoryInfo subDirectory in subDirectories)
                totalSize += CalculateFolderSize(subDirectory.FullName, totalSize);

            return totalSize;
        }

        private static string PathFormart(string path)
        {
            path = path.Replace('\\', Path.DirectorySeparatorChar)
                       .Replace('/', Path.DirectorySeparatorChar);

            return path;
        }

        /// <summary>
        /// 转换文件大小
        /// </summary>
        private string GetFileSizeStr(long size)
        {
            // 定义文件大小的单位  
            string[] units = new string[] { "B", "KB", "MB", "GB", "TB" };

            // 计算文件大小并自动转换单位  
            double fileSize = size;
            int unitIndex = 0;
            while (fileSize >= 1024 && unitIndex < units.Length - 1)
            {
                fileSize /= 1024;
                unitIndex++;
            }

            return fileSize.ToString("0.00") + units[unitIndex];
        }

        /// <summary>
        /// 递归获取文件夹内所有压缩文件
        /// </summary>
        private void GetDecompressFilesByDir(string dir, List<System.IO.FileInfo> files)
        {
            var dirInfo = new DirectoryInfo(dir);

            // 读取目录文件
            var thisFiles = dirInfo.GetFiles();
            foreach (var thisFile in thisFiles)
            {
                var fileEx = thisFile.Extension.ToLower();
                if (fileEx == ".zip" || fileEx == ".7z" || fileEx == ".rar")
                    files.Add(thisFile);
            }

            // 读取文件夹并递归
            var thisDirs = dirInfo.GetDirectories();
            foreach (var thisDir in thisDirs)
                this.GetDecompressFilesByDir(thisDir.FullName, files);
        }

        #endregion
    }
}
